home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / OTSimpleDownloadHTTP / OTSimpleDownloadHTTP.c next >
Encoding:
Text File  |  2000-09-28  |  8.7 KB  |  298 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        OTSimpleDownloadHTTP.c
  3.  
  4.     Contains:    Implementation of the simple HTTP download sample.
  5.  
  6.     Written by: Quinn "The Eskimo!"    
  7.  
  8.     Copyright:    Copyright © 1997-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 7/23/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.  
  24. /////////////////////////////////////////////////////////////////////
  25. // The OT debugging macros in <OTDebug.h> require this variable to
  26. // be set.
  27. #include <Files.h>
  28. #include <TextUtils.h>
  29.  
  30. #ifndef qDebug
  31. #define qDebug    1
  32. #endif
  33.  
  34. /////////////////////////////////////////////////////////////////////
  35. // Pick up all the standard OT stuff.
  36.  
  37. #include <OpenTransport.h>
  38.  
  39. /////////////////////////////////////////////////////////////////////
  40. // Pick up all the OT TCP/IP stuff.
  41.  
  42. #include <OpenTptInternet.h>
  43.  
  44. /////////////////////////////////////////////////////////////////////
  45. // Pick up the OTDebugBreak and OTAssert macros.
  46.  
  47. #include <OTDebug.h>
  48.  
  49. /////////////////////////////////////////////////////////////////////
  50. // Pick up YieldToAnyThread.
  51.  
  52. #include <Threads.h>
  53.  
  54. /////////////////////////////////////////////////////////////////////
  55. // Pick up our own prototype.
  56.  
  57. #include "OTSimpleDownloadHTTP.h"
  58.  
  59. /////////////////////////////////////////////////////////////////////
  60. // OTDebugStr is not defined in any OT header files, but it is
  61. // exported by the libraries, so we define the prototype here.
  62.  
  63. extern pascal void OTDebugStr(const char* str);
  64.  
  65. /////////////////////////////////////////////////////////////////////
  66.  
  67. enum {
  68.     kTransferBufferSize = 4096
  69. };
  70.  
  71. /////////////////////////////////////////////////////////////////////
  72.  
  73. static pascal void YieldingNotifier(void* contextPtr, OTEventCode code, 
  74.                                        OTResult result, void* cookie)
  75.     // This simple notifier checks for kOTSyncIdleEvent and
  76.     // when it gets one calls the Thread Manager routine
  77.     // YieldToAnyThread.  Open Transport sends kOTSyncIdleEvent
  78.     // whenever it's waiting for something, eg data to arrive
  79.     // inside a sync/blocking OTRcv call.  In such cases, we
  80.     // yield the processor to some other thread that might
  81.     // be doing useful work.
  82. {
  83.     #pragma unused(contextPtr)
  84.     #pragma unused(result)
  85.     #pragma unused(cookie)
  86.     OSStatus junk;
  87.     
  88.     switch (code) {
  89.         case kOTSyncIdleEvent:
  90.             junk = YieldToAnyThread();
  91.             OTAssert("YieldingNotifier: YieldToAnyThread failed", junk == noErr);
  92.             break;
  93.         default:
  94.             // do nothing
  95.             break;
  96.     }
  97. }
  98.  
  99. /////////////////////////////////////////////////////////////////////
  100.  
  101. OSStatus DownloadHTTPSimple(const char *hostName,
  102.                             const char *httpCommand,
  103.                             const short destFileRefNum)
  104.     // Download a URL from the a web server.  hostName is a pointer
  105.     // to a string that contains the DNS address of the web server.
  106.     // The DNS address must be suffixed by ":<port>", where <port>
  107.     // is the port number the web server is operating on.
  108.     // httpCommand contains the HTTP command to send.  Typically this
  109.     // is of the form:
  110.     //
  111.     //        GET <x> HTTP/1.0\0x0d\0x0a\0x0d\0x0a
  112.     //
  113.     // where <x> is the URL path.  destFileRefNum is the file
  114.     // reference number to which the results of the HTTP command
  115.     // are written.  This routine does not parse the returned HTTP
  116.     // header in any way.  The entire incoming stream goes into
  117.     // the file verbatim.
  118.     //
  119.     // For example, if you were asked to download a URL like:
  120.     //
  121.     //        http://devworld.apple.com/dev/technotes.shtml
  122.     //
  123.     // you would set:
  124.     //
  125.     //         o hostName to "devworld.apple.com:80" (80 is the
  126.     //          default port for HTTP.
  127.     //        o httpCommand to "GET /dev/technotes.shtml HTTP/1.0\0x0d\0x0a\0x0d\0x0a"
  128. {
  129.     OSStatus     err;
  130.     OSStatus     junk;
  131.     Ptr            transferBuffer     = nil;
  132.     EndpointRef ep                 = kOTInvalidEndpointRef;
  133.     TCall         sndCall;
  134.     DNSAddress     hostDNSAddress;
  135.     OTFlags     junkFlags;
  136.     OTResult     bytesSent;
  137.     OTResult     bytesReceived;
  138.     OTResult     lookResult;
  139.     Boolean        bound            = false;
  140.     
  141.     // First allocate a buffer for storing the data as we read it.
  142.     
  143.     err = noErr;
  144.     transferBuffer = OTAllocMem(kTransferBufferSize);
  145.     if ( transferBuffer == nil ) {
  146.         err = kENOMEMErr;
  147.     }
  148.     
  149.     // Now open a TCP endpoint.
  150.     
  151.     if (err == noErr) {
  152.         ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err);
  153.     }
  154.     
  155.     // If the endpoint opens successfully...
  156.     
  157.     if (err == noErr) {
  158.  
  159.         // Establish the modes of operation.  This sample uses
  160.         // sync/blocking mode, with sync idle events that yield
  161.         // time using the Thread Manager.
  162.  
  163.         junk = OTSetSynchronous(ep);
  164.         OTAssert("DownloadHTTPSimple: OTSetSynchronous failed", junk == noErr);
  165.         
  166.         junk = OTSetBlocking(ep);
  167.         OTAssert("DownloadHTTPSimple: OTSetBlocking failed", junk == noErr);
  168.         
  169.         junk = OTInstallNotifier(ep, YieldingNotifier, nil);
  170.         OTAssert("DownloadHTTPSimple: OTInstallNotifier failed", junk == noErr);
  171.         
  172.         junk = OTUseSyncIdleEvents(ep, true);
  173.         OTAssert("DownloadHTTPSimple: OTUseSyncIdleEvents failed", junk == noErr);
  174.  
  175.         // Bind the endpoint.  Because we're an outgoing connection,
  176.         // we don't have to bind it to a specific address.
  177.                 
  178.         err = OTBind(ep, nil, nil);
  179.         bound = (err == noErr);
  180.     }
  181.         
  182.     // Initialise the sndCall structure and call OTConnect.  We nil
  183.     // out most of the fields in the sndCall structure because
  184.     // we don't want any special options or connection data.
  185.     // The important field of the sndCall is the addr TNetBuf,
  186.     // which we initialise to the
  187.     
  188.     if (err == noErr) {
  189.         sndCall.addr.buf     = (UInt8 *) &hostDNSAddress;
  190.         sndCall.addr.len     = OTInitDNSAddress(&hostDNSAddress, (char *) hostName);
  191.         sndCall.opt.buf     = nil;        // no connection options
  192.         sndCall.opt.len     = 0;
  193.         sndCall.udata.buf     = nil;        // no connection data
  194.         sndCall.udata.len     = 0;
  195.         sndCall.sequence     = 0;        // ignored by OTConnect
  196.         
  197.         err = OTConnect(ep, &sndCall, nil);
  198.     }
  199.     
  200.     // Send the HTTP command to the server.
  201.     
  202.     if (err == noErr) {
  203.         bytesSent = OTSnd(ep, (void *) httpCommand, OTStrLength(httpCommand), 0);
  204.         
  205.         // OTSnd returns the number of bytes sent.  Because we're in
  206.         // synchronous mode, it won't return until it's sent all the
  207.         // bytes, or it gets an error.
  208.         
  209.         if (bytesSent > 0) {
  210.             err = noErr;
  211.         } else {
  212.             err = bytesSent;
  213.         }
  214.     }
  215.     
  216.     // Now that we have sent the HTTP command, we turn around and
  217.     // receive the data comming back from the server.
  218.     
  219.     if (err == noErr) {
  220.         do {
  221.             bytesReceived = OTRcv(ep, (void *) transferBuffer, kTransferBufferSize, &junkFlags);
  222.             
  223.             // OTRcv returns the number of bytes received.  Because we're in
  224.             // synchronous mode, it won't return until it's sent all the
  225.             // bytes, or it gets an error.  
  226.             
  227.             if (bytesReceived > 0) {
  228.                 err = FSWrite(destFileRefNum, &bytesReceived, transferBuffer);
  229.             } else {
  230.                 err = bytesReceived;
  231.             }
  232.             
  233.             // We keep running this loop until we get an error.
  234.     
  235.         } while (err == noErr);
  236.     }
  237.  
  238.     // Now we handle the various forms of error we can get.  The
  239.     // most common in kOTLookErr.  This means that some event
  240.     // has happened that we need to look at.  We call OTLook
  241.     // to get the event code and then handle the various types
  242.     // of event appropriately.
  243.     
  244.     if (err == kOTLookErr) {
  245.  
  246.         lookResult = OTLook(ep);
  247.  
  248.         switch (lookResult) {
  249.  
  250.             case T_DISCONNECT:
  251.                 // If we get a T_DISCONNECT event, the remote peer
  252.                 // has disconnected the stream in a dis-orderly
  253.                 // fashion.  HTTP servers will often just disconnect
  254.                 // a connection like this to indicate the end of the
  255.                 // data, so all we need do is clear the T_DISCONNECT
  256.                 // event on the endpoint.
  257.  
  258.                 err = OTRcvDisconnect(ep, nil);
  259.                 break;
  260.                 
  261.             case T_ORDREL:
  262.                 // If we get a T_ORDREL event, the remote peer
  263.                 // has disconnected the stream in an orderly
  264.                 // fashion.  This orderly disconnect indicates that
  265.                 // the end of the data.  We respond by clearing
  266.                 // the T_ORDREL, and then calling OTSndOrderlyDisconnect
  267.                 // to acknowledge the orderly disconnect at
  268.                 // the remote peer.
  269.                 
  270.                 err = OTRcvOrderlyDisconnect(ep);
  271.                 if (err == noErr) {
  272.                     err = OTSndOrderlyDisconnect(ep);
  273.                 }
  274.                 break;
  275.                 
  276.             default:
  277.                 // Leave err as kOTLookErr.
  278.                 break;
  279.         }
  280.     }
  281.  
  282.     if ( (err == noErr) && bound ) {
  283.         junk = OTUnbind(ep);
  284.         OTAssert("DownloadHTTPSimple: OTUnbind failed.", junk == noErr);
  285.     }
  286.     
  287.     // Clean up.
  288.     if (ep != kOTInvalidEndpointRef) {
  289.         junk = OTCloseProvider(ep);
  290.         OTAssert("DownloadHTTPSimple: OTCloseProvider failed.", junk == noErr);
  291.     }
  292.     if (transferBuffer != nil) {
  293.         OTFreeMem(transferBuffer);
  294.     }
  295.     
  296.     return (err);
  297. }
  298.